/**
 * @file main.c
 * @brief I2C1读取MPU6050数据并通过UART2输出的示例。
 * @author abu (abu@eswincomputing.com)
 * @date 2023-01-10
 *
 * @details 本示例通过I2C1读取MPU6050的加速度、角速度和温度原始数据，并通过UART2周期性输出。
 * MPU6050的AD0引脚连接PORTD2（78脚，GPIO，默认拉低，I2C地址0x68），INT引脚连接PORTD4（79脚，GPIO/INT，配置为输入）。
 * I2C1的SCL/SDA引脚配置见peripherals_i2c_0_config.c和pin_config.c。
 *
 * @pins
 * | 功能名称      | 端口号    | 复用功能 | 板载引脚 |
 * |:-------------|:----------|:---------|:---------|
 * | I2C0 SCL     | PORTC25   | ALT3     | 72脚     |
 * | I2C0 SDA     | PORTC26   | ALT3     | 73脚     |
 * | MPU6050 AD0  | PORTD2    | GPIO     | 78脚     |
 * | MPU6050 INT  | PORTD4    | GPIO/INT | 79脚     |
 *
 * @connections
 *
 * @attention
 * 1. 请确保MPU6050模块已正确连接至I2C1总线，AD0引脚拉低（I2C地址0x68）。
 * 2. UART2需连接至PC串口工具以查看输出数据。
 * 3. 若MPU6050初始化或读取失败，将有中文提示信息输出。
 *
 * 修改历史：
 * 日期         版本      作者      变更内容
 * 2023-01-10   v1.0      abu      初始版本，基于EEPROM示例改为MPU6050读取与输出
 */

#include "../board/sdk_project_config.h"
#include <stdio.h>
#include <string.h>

#define NUM_OF_I2C_CONFIGURED_PINS0_FOR_GPIO 4U
#define OSIF_WAIT_FOREVER 0xFFFFFFFFU
i2c_master_state_t g_stI2C0MasterState;

// MPU6050相关宏定义
#define MPU6050_I2C_ADDR         (0x68)
#define MPU6050_REG_PWR_MGMT_1   (0x6B)
#define MPU6050_REG_ACCEL_XOUT_H (0x3B)
#define MPU6050_DATA_LEN         (14)

static uint8_t mpu6050_data[MPU6050_DATA_LEN];

// 设置I2C目标地址
static void MPU6050_SetI2CAddr(void)
{
    I2C_DRV_MasterSetSlaveAddr(INST_I2C_0, MPU6050_I2C_ADDR, false);
}

// MPU6050初始化（唤醒）
static int MPU6050_Init(void)
{
    uint8_t buf[2];
    buf[0] = MPU6050_REG_PWR_MGMT_1;
    buf[1] = 0x00; // 唤醒
    MPU6050_SetI2CAddr();
    if (I2C_DRV_MasterSendDataBlocking(INST_I2C_0, buf, 2, true, OSIF_WAIT_FOREVER) != STATUS_SUCCESS) {
        return -1;
    }
    OS_DelayMs(10);
    return 0;
}

// 读取MPU6050的14字节原始数据
static int MPU6050_ReadData(uint8_t *data)
{
    uint8_t reg = MPU6050_REG_ACCEL_XOUT_H;
    MPU6050_SetI2CAddr();
    if (I2C_DRV_MasterSendDataBlocking(INST_I2C_0, &reg, 1, false, OSIF_WAIT_FOREVER) != STATUS_SUCCESS) {
        return -1;
    }
    if (I2C_DRV_MasterReceiveDataBlocking(INST_I2C_0, data, MPU6050_DATA_LEN, true, OSIF_WAIT_FOREVER) != STATUS_SUCCESS) {
        return -2;
    }
    return 0;
}

// 输出MPU6050物理量数据（中文）
static void MPU6050_PrintPhysicalData(const uint8_t *data)
{
    int16_t ax_raw = (data[0] << 8) | data[1];
    int16_t ay_raw = (data[2] << 8) | data[3];
    int16_t az_raw = (data[4] << 8) | data[5];
    int16_t temp_raw = (data[6] << 8) | data[7];
    int16_t gx_raw = (data[8] << 8) | data[9];
    int16_t gy_raw = (data[10] << 8) | data[11];
    int16_t gz_raw = (data[12] << 8) | data[13];

    float ax = ax_raw / 16384.0f;
    float ay = ay_raw / 16384.0f;
    float az = az_raw / 16384.0f;
    float temp = temp_raw / 340.0f + 36.53f;
    float gx = gx_raw / 131.0f;
    float gy = gy_raw / 131.0f;
    float gz = gz_raw / 131.0f;

    printf("加速度: X=%.3fg Y=%.3fg Z=%.3fg\r\n", ax, ay, az);
    printf("角速度: X=%.3f°/s Y=%.3f°/s Z=%.3f°/s\r\n", gx, gy, gz);
    printf("温度: %.2f°C\r\n", temp);
}

// 初始化MPU6050 AD0和INT引脚
static void MPU6050_PinInit(void)
{
    // PTD2（78脚）为AD0，配置为输出，拉低（0x68）或拉高（0x69），此处默认拉低
    PINS_DRV_WritePinDirection(PORTD, 2, GPIO_OUTPUT_DIRECTION);
    PINS_DRV_WritePin(PORTD, 2, 0); // 拉低，I2C地址为0x68
    // PTD4（79脚）为INT，配置为输入
    PINS_DRV_WritePinDirection(PORTD, 4, GPIO_INPUT_DIRECTION);
}

/**
 * @brief PINMUX configuration for I2C0 and I2C1 pins for GPIO mode.
 */
static pin_settings_config_t g_stI2cPinmuxForGpioInitConfigArr0[NUM_OF_I2C_CONFIGURED_PINS0_FOR_GPIO] = {
    {
        .base           = PORTC,
        .pinPortIdx     = 25U, // FullMask evb 72 pin
        .pullConfig     = PORT_INTERNAL_PULL_NOT_ENABLED,
        .driveSelect    = PORT_STR0_DRIVE_STRENGTH,
        .mux            = PORT_MUX_ALT1,
        .isGpio         = true,
        .direction      = GPIO_OUTPUT_DIRECTION,
        .initValue      = 1,
        .intConfig      = PORT_INT_DISABLED,
        .clearIntFlag   = false,
        .debounceEnable = true,
    },
    {
        .base           = PORTC,
        .pinPortIdx     = 26U, // FullMask evb 73 pin
        .pullConfig     = PORT_INTERNAL_PULL_NOT_ENABLED,
        .driveSelect    = PORT_STR0_DRIVE_STRENGTH,
        .mux            = PORT_MUX_ALT1,
        .isGpio         = true,
        .direction      = GPIO_OUTPUT_DIRECTION,
        .initValue      = 1,
        .intConfig      = PORT_INT_DISABLED,
        .clearIntFlag   = false,
        .debounceEnable = true,
    },
    {
        .base           = PORTB,
        .pinPortIdx     = 4U, // FullMask evb 45 pin
        .pullConfig     = PORT_INTERNAL_PULL_NOT_ENABLED,
        .driveSelect    = PORT_STR0_DRIVE_STRENGTH,
        .mux            = PORT_MUX_ALT1,
        .isGpio         = true,
        .direction      = GPIO_OUTPUT_DIRECTION,
        .initValue      = 1,
        .intConfig      = PORT_INT_DISABLED,
        .clearIntFlag   = false,
        .debounceEnable = true,
    },
    {
        .base           = PORTB,
        .pinPortIdx     = 5U, // FullMask evb 46 pin
        .pullConfig     = PORT_INTERNAL_PULL_NOT_ENABLED,
        .driveSelect    = PORT_STR0_DRIVE_STRENGTH,
        .mux            = PORT_MUX_ALT1,
        .isGpio         = true,
        .direction      = GPIO_OUTPUT_DIRECTION,
        .initValue      = 1,
        .intConfig      = PORT_INT_DISABLED,
        .clearIntFlag   = false,
        .debounceEnable = true,
    },
};

/**
 * @brief Use gpio mode to sent 9 clk on scl line to release peripherals.
 * Because used 24LC128-I/SN eeprom will lock scl when end transform data with error.
 * Use I2C0 and I2C1 pins in gpio mode and send 9 scl to eeprom to release scl lock.
 */
static void ReleasePeripherals(void)
{
    PINS_DRV_Init(NUM_OF_I2C_CONFIGURED_PINS0_FOR_GPIO, g_stI2cPinmuxForGpioInitConfigArr0);
    int i = 9;
    while (i) {
        PINS_DRV_WritePin(PORTC, 25, 0);
        PINS_DRV_WritePin(PORTB, 04, 0);
        OS_DelayMs(10);
        PINS_DRV_WritePin(PORTC, 25, 1);
        PINS_DRV_WritePin(PORTB, 04, 1);
        OS_DelayMs(10);
        i--;
    }
}

/**
 * @brief The main function for the project.
 */
int main(void)
{
    CLOCK_SYS_Init(g_pstClockManConfigsArr[0]);
    // 初始化PDMA、PINMUX、UART2、I2C0
    PDMA_DRV_Init(&g_stPdmaState0, &g_stPdma0UserConfig0, g_stPdma0ChnStateArray,
                  g_stPdma0ChannelConfigArray, PDMA_CHANNEL_CONFIG_COUNT);
    PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_stPinmuxConfigArr);
    UART_DRV_Init(INST_UART_2, &g_stUartState_2, &g_stUart2UserConfig0);
    setLogPort(2);
    I2C_DRV_MasterInit(INST_I2C_0, &g_stI2c0MasterUserConfig0, &g_stI2C0MasterState);
    MPU6050_PinInit();

    // 初始化MPU6050
    if (MPU6050_Init() != 0) {
        printf("MPU6050 初始化失败!\r\n");
        while (1) { OS_DelayMs(1000); }
    } else {
        printf("MPU6050 初始化成功!\r\n");
    }

    while (1) {
        if (MPU6050_ReadData(mpu6050_data) == 0) {
            MPU6050_PrintPhysicalData(mpu6050_data);
        } else {
            printf("MPU6050 读取失败!\r\n");
        }
        OS_DelayMs(500);
    }
}
